﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Linq;
using ArtMath.Command;
using ArtMath.Component;
using ArtMath.Component.CoordinateSystem;
using ArtMath.Resource;
using ArtMath.CommonType;
using ArtMath.Flag;
using ArtMath.Setting;

namespace ArtMath.Control
{
    public partial class LayerControl : UserControl, IActivate, ISerializable//, IList<LayerItem>
    {
        [Browsable(false)]
        public List<LayerItem> Items { get; } = new List<LayerItem>();
        [Browsable(false)]
        public List<LayerItem> SelectedItems { get; } = new List<LayerItem>();
        [Browsable(false)]
        public bool IsActivated { get; private set; }
        int selectedOnceCount = 0;
        public bool ClickAfterAdd = true;
        public SketchPad Pad { get; }
        public event Action<LayerControl, RefreshWindowsStatusEventArgs> RefreshWindowsStatus;
        public event Action<LayerControl> Activated;
        const string ObjectItemsKey = "ObjectItems";
        public LayerControl(SketchPad pad)
        {
            InitializeComponent();
            Pad = pad;
            Dock = DockStyle.Fill;
        }
        public void BeforeSerializing(Dictionary<string, object> data)
        {
            data.Add(Pad.Name + "." + ObjectItemsKey, Items.Where((LayerItem item) => { return !item.LinkPad; }).ToArray());
        }
        public void AfterDeserialized(Dictionary<string, object> data)
        {
            IEnumerable<LayerItem> items = data[Pad.Name + "." + ObjectItemsKey] as IEnumerable<LayerItem>;
            foreach (LayerItem item in items)
            {
                Add(item);
                //item.ObjectState = item.LinkedObj.ObjectState;
                item.Selected_ = false;
            }
        }
        public void Activate()
        {
            if (Pad.IsActivated)
            {
                IsActivated = true;
                Activated?.Invoke(this);
            }
            else
            {
                Pad.Activate();
            }
        }
        /// <summary>
        /// 获取或设置指定索引处的列表项（自动过滤已删除列表）
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public LayerItem this[int index]
        {
            get
            {
                while (index >= 0 && index < Items.Count && Items[index].ObjectState == ObjectState.Deleted)
                {
                    index++;
                }
                return index >= Items.Count ? null : Items[index];
            }
            set
            {
                while (index >= 0 && index < Items.Count && Items[index].ObjectState == ObjectState.Deleted)
                {
                    index++;
                }
                if (index < Items.Count) Items[index] = value;
            }
        }
        public int SelectedOnceCount
        {
            get { return selectedOnceCount; }
            set
            {
                selectedOnceCount = value;
                SelectCount(selectedOnceCount);
            }
        }
        public void Add(LayerItem item)
        {
            item.Layer = this;
            if (ClickAfterAdd)//切换坐标系保持属性编辑器选中画板，否则编辑器值改变事件出错
            {
                item.Item_Click(false);
            }
            else
            {
                RefreshStatus();
            }
        }
        public void AddRange(IEnumerable<LayerItem> items)
        {
            foreach (LayerItem item in items)
            {
                Add(item);
            }
        }
        public void Remove(LayerItem item)
        {
            item.Layer = null;
            RefreshStatus();
        }
        public IList<Base> Delete(bool all = false, bool recoverable = false)
        {
            List<Base> objs = new List<Base>();
            foreach (LayerItem item in all ? Items : SelectedItems)
            {
                if (item.LinkedObj == null) continue;
                objs.Add(item.LinkedObj);
            }
            return Pad.Delete(objs, recoverable);
        }
        public static void DeleteObjectHandler<T>(ICollection<T> objs) where T : IRecoverableDelete
        {
            CoordinateBase cs = null;
            HashSet<T> deletedObjs = new HashSet<T>();
            foreach (T item in objs)
            {
                if (item.ObjectState == ObjectState.Deleted)
                {
                    deletedObjs.Add(item);
                    continue;
                }
                CoordinateBase tmp = item as CoordinateBase;
                if (tmp != null)
                {
                    cs = tmp;
                    deletedObjs.Add(item);
                }
                else
                {
                    try
                    {
                        item.Delete(true);
                        if (item.ObjectState == ObjectState.Deleted)
                        {
                            deletedObjs.Add(item);
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        break;
                    }
                    catch (Exception ex)
                    {
                        if (cs != null)
                        {
                            cs.Pad.ChangeCoordinate(CoordinateSystemType.None, true);
                        }
                        ex.Data.Add("DeletedObjects", deletedObjs);
                        throw ex;
                    }
                }
            }
            if (cs != null)
            {
                cs.Pad.ChangeCoordinate(CoordinateSystemType.None, true);
            }
            if (deletedObjs.Count < objs.Count)
            {
                Exception ex = new Exception(Flags.ExceptionShieldMessage);
                ex.Data.Add("DeletedObjects", deletedObjs);
                throw ex;
            }
        }
        public static void RecoverObjectHandler<T>(ICollection<T> objs) where T : IRecoverableDelete
        {
            //坐标系一定要放在最后恢复。因为如果先恢复坐标系，那么objs中的点将切换类型，导致真正要恢复的点不在objs里，objs里的点已经作废了
            CoordinateBase cs = null;
            HashSet<T> recoveredObjs = new HashSet<T>();
            foreach (T item in objs)
            {
                if (item.ObjectState == ObjectState.Normal)
                {
                    recoveredObjs.Add(item);
                    continue;
                }
                CoordinateBase tmp = item as CoordinateBase;
                if (tmp != null)
                {
                    cs = tmp;
                    recoveredObjs.Add(item);
                }
                else
                {
                    try
                    {
                        item.Recover();
                        if (item.ObjectState == ObjectState.Normal)
                        {
                            recoveredObjs.Add(item);
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        break;
                    }
                    catch (Exception ex)
                    {
                        if (cs != null)
                        {
                            cs.Pad.ChangeCoordinate(() =>
                            {
                                cs.Recover();
                            });
                        }
                        ex.Data.Add("RecoveredObjects", recoveredObjs);
                        throw ex;
                    }
                }
            }
            if (cs != null)
            {
                cs.Pad.ChangeCoordinate(() =>
                {
                    cs.Recover();
                });
            }
            if (recoveredObjs.Count < objs.Count)
            {
                Exception ex = new Exception(Flags.ExceptionShieldMessage);
                ex.Data.Add("RecoveredObjects", recoveredObjs);
                throw ex;
            }
        }
        /// <summary>
        /// 在列表中自动选择最后一个可见的列表项
        /// </summary>
        public void AutoSelect()
        {
            if (Items.Count == 0) return;
            int i = Items.Count - 1;
            while (i >= 0 && Items[i--].ObjectState == ObjectState.Deleted) ;
            if (i >= -1)
            {
                Items[i + 1].Selected_ = true;
            }
        }
        public void SelectCount(int sum)
        {
            while (SelectedItems.Count > sum)
            {
                SelectedItems[0].Selected_ = false;
            }
        }
        public void SelectNone()
        {
            SelectCount(0);
        }
        public void RefreshStatus()
        {
            if (SelectedItems.Count == 0)
            {
                //if (Pad.ChangeInvertionState.Enabled)
                //{
                //    Pad.ChangeInvertionState.Stop();
                //}
                RefreshWindowsStatus?.Invoke(this, new RefreshWindowsStatusEventArgs(false, false, false, null));
            }
            else
            {
                object[] objs = new object[SelectedItems.Count];
                bool enableUp = true, enableDown = true, enableFmt = true;
                for (int i = 0; i < objs.Length; i++)
                {
                    if (SelectedItems[i].LinkedObj == null)
                    {
                        //只要选中项中含有画板项，上下移动就被禁用了
                        objs[i] = Pad;
                        enableUp = enableDown = enableFmt = false;
                    }
                    else
                    {
                        objs[i] = SelectedItems[i].LinkedObj;
                        if (enableDown)
                        {
                            if (BeforeCount(SelectedItems[i]) == 0) enableDown = false;
                        }
                        if (enableUp)
                        {
                            if (AfterCount(SelectedItems[i]) == 0) enableUp = false;
                        }
                    }
                }
                RefreshWindowsStatus?.Invoke(this, new RefreshWindowsStatusEventArgs(enableUp, enableDown, enableFmt, objs));
                //if (!Pad.ChangeInvertionState.Enabled && Settings.Instance.FlashInterval > 0)
                //{
                //    Pad.ChangeInvertionState.Start();
                //}
            }
        }
        public int BeforeCount(LayerItem item)
        {
            int sum = 0, index = item.Index;
            for (int i = 0; i < index; i++)
            {
                if (Items[i].LinkedObj == null) continue;
                if (Items[i].LinkedObj.Visible)
                {
                    sum++;
                }
            }
            return sum;
        }
        public int AfterCount(LayerItem item)
        {
            int sum = 0;
            for (int i = item.Index + 1; i < Items.Count; i++)
            {
                if (Items[i].LinkedObj == null) continue;
                if (Items[i].LinkedObj.Visible)
                {
                    sum++;
                }
            }
            return sum;
        }
        private void LayerControl_EnabledChanged(object sender, EventArgs e)
        {
            BackColor = Enabled ? Color.White : Color.FromArgb(240, 240, 240);
        }
        private void LayerControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                SelectNone();
            }
        }

        public void Deactivate()
        {
            IsActivated = false;
        }

        public class RefreshWindowsStatusEventArgs : EventArgs
        {
            public bool MoveUpEnabled { get; }
            public bool MoveDownEnabled { get; }
            public bool FormatPainterEnabled { get; }
            public object[] SelectedObjects { get; }
            //public int ValidItemCount { get; }
            public RefreshWindowsStatusEventArgs(bool up, bool down, bool fmt, object[] objs)//, int validCnt)
            {
                MoveUpEnabled = up;
                MoveDownEnabled = down;
                FormatPainterEnabled = fmt;
                SelectedObjects = objs;
                //ValidItemCount = validCnt;
            }
        }
    }
}
